package guet.imagan.progbuilder;

import guet.imagan.progbuilder.impl.ExecuteFileForWindow;

import java.util.List;

/**
 * 项目构建器
 */
public class ProjectBuilder implements IBuilder {
	private long timeout = 1;

	/**
	 * 项目构建类型类，有三种，分别为编译型，混合型和解释型
	 */
	public enum BuilderType {
		COMPILE, MIXED, INTERPRETE
	}

	/**
	 * 项目构建器中需要用到的编译器
	 */
	private Compiler compiler;
	/**
	 * 项目构建器中需要用到的解释器
	 */
	private Interpreter interpreter;
	/**
	 * 表示构建器类型
	 */
	private final BuilderType builderType;

	public Compiler getCompiler() {
		return compiler;
	}

	public Interpreter getInterpreter() {
		return interpreter;
	}

	/**
	 * 返回当前的构建器类型
	 *
	 * @return
	 */
	public BuilderType getBuilderType() {
		return this.builderType;
	}

	/**
	 * 为纯编译型语言（例如，C语言，C++语言等）创建一个项目构建器
	 *
	 * @param compiler 编译器
	 */
	public ProjectBuilder(Compiler compiler) {
		this.compiler = compiler;
		compiler.setTimeout(timeout);
		this.builderType = BuilderType.COMPILE;
	}

	/**
	 * 为混合编程语言（例如Java语言，kotlin语言）创建一个项目构建器
	 *
	 * @param compiler    编译器
	 * @param interpreter 解释器
	 */
	public ProjectBuilder(Compiler compiler, Interpreter interpreter) {
		this.compiler = compiler;
		this.interpreter = interpreter;
		this.compiler.setTimeout(timeout);
		this.interpreter.setTimeout(timeout);
		builderType = BuilderType.MIXED;

	}

	/**
	 * 为纯解释型的编程语言（如python）创建一个项目构建器
	 *
	 * @param interpreter 解释器
	 */
	public ProjectBuilder(Interpreter interpreter) {
		this.interpreter = interpreter;
		this.interpreter.setTimeout(timeout);
		builderType = BuilderType.INTERPRETE;
	}

	/**
	 * 在新的窗口以阻塞的方式运行项目，即：只要该窗口没有关闭，该方法就不会返回。
	 * 说明：该方法将根据需要决定是否构建项目，生成目标文件，然后执行程序。
	 *
	 * @param project 待运行的项目
	 * @param args    启动运行时，传入的命令行参数，这个参数将作为main函数或方法的实参传入。
	 * @throws CompilerException    当项目编译失败时
	 * @throws InterpreterException 当项目解释失败时
	 * @throws ExecuteFileException 当运行程序失败时
	 */
	@Override
	public void run(Project project, String... args) throws CompilerException, InterpreterException, ExecuteFileException, Project.ProjectException {
		List<String> compileResult;
		switch (builderType) {
			case COMPILE:
				compileResult = compiler.compile(project);
				ExecuteFile executeFile = new ExecuteFileForWindow(compileResult.get(0));
				executeFile.executeWithWindow(args);
				break;
			case INTERPRETE:
				List<String> errLines = interpreter.checkSyntaxErr(project);
				if (errLines == null)
					interpreter.executeWithWindow(project, args);
				else {
					StringBuilder err = new StringBuilder();
					errLines.forEach(s -> {
						err.append(s + "\n");
					});
					throw new InterpreterException(err.toString());
				}
				break;
			default:
				compiler.compile(project);
				interpreter.executeWithWindow(project, args);
		}
	}

	/**
	 * 在后台以非阻塞的方式运行项目，这种方式无法看到运行程序的窗口。这种方式适合命令行程序的测试。
	 *
	 * @param project 待运行的项目
	 * @param input   测试输入，这个输入将被作为程序运行时控制台的输入内容
	 * @param args    启动运行时，传入的命令行参数，这个参数将作为main函数或方法的实参传入。
	 * @return 程序的控制台输出
	 * @throws CompilerException    当项目编译失败时
	 * @throws InterpreterException 当项目解释失败时
	 * @throws ExecuteFileException 当运行程序失败时
	 */
	@Override
	public TextFile run(Project project, TextFile input, String... args) throws CompilerException,
			InterpreterException, ExecuteFileException, Project.ProjectException {
		List<String> compileResult;
		switch (builderType) {
			case COMPILE:
				compileResult = compiler.compile(project);
				ExecuteFile executeFile = new ExecuteFileForWindow(compileResult.get(0));
				return executeFile.executeWithoutWindow(input, args);
			case INTERPRETE:
				List<String> err = interpreter.checkSyntaxErr(project);
				if (err == null) {
					return interpreter.executeWithoutWindow(project, input, args);
				}
				StringBuilder sb = new StringBuilder();
				err.forEach(s -> {
					sb.append(s + "\n");
				});
				throw new InterpreterException(sb.toString());
			default:
				compiler.compile(project);
				return interpreter.executeWithoutWindow(project, input, args);
		}
	}

}
